home *** CD-ROM | disk | FTP | other *** search
- /* DateDialog.c */
- /* an example of how to create a date-adjuster type thing */
- /* as used in the General control panel */
-
- /* C.K. Haun <TR> */
- #include "DateThing.h"
- /* protos for this file */
-
- static ControlHandle SnatchHandle(DialogPtr thebox, short theGetItem);
- static void WritePicturedNum(long theNum, short width);
- static pascal void BorderDefault(DialogPtr dwind, short dinum);
- static void DrawIndString(short strList, short index);
- static pascal void DateBox(WindowPtr rDial, short dinum);
- static void AdjustDate(short incOr, DialogPtr rDial, short newVal);
- static pascal Boolean DateThingerFilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);
-
- /* exported */
- void DateSetterDialog(void);
-
- /* I like to keep the seconds and the 'real' representation of the date all the */
- /* time instead of doing a lot of conversions */
- /* so this struct */
-
- typedef struct DateStruct {
- Boolean inUse;
- unsigned long startDateraw;
- unsigned long endDateraw;
- DateTimeRec startDate;
- DateTimeRec endDate;
- } DateStruct, *DatePtr, **DateHdl;
-
- typedef struct dateBoxStruct {
- DateStruct dialogDate; /* contains the current start and end dates */
- Rect charBox[6]; /* rectangles containing the numbers */
- Rect arrowHits[2]; /* test if the user is in my arrows */
- Boolean editHot; /* if this is active. aways true in this example */
- /* this is in here if you also have an edit line in the dialog */
- /* and you need to direct keys to that edit line instead of the */
- /* date thing sometimes */
- short whichHitting; /* what month/day etc. are we currently in? */
- Rect thing; /* where the thing goes */
- long lastLetterTick; /* when was the last time they typed something */
- short lastWhichHit; /* in what area? */
- } dateBoxStruct;
- static dateBoxStruct dialogDateStruct;
-
- enum {
- kManDateItem = 3, kBorderBox
- };
-
- enum {
- kStartMonth = 0, kStartDay, kStartYear, kEndMonth, kEndDay, kEndYear
- };
- #pragma segment Main
-
- /* Gets the ControlHandle for the item you want in the dialog box thebox. */
- /* Handy for setting checkboxes and radio buttons */
-
- static ControlHandle SnatchHandle(DialogPtr thebox, short theGetItem)
- {
- short itemtype;
- Rect itemrect;
- Handle thandle;
-
- GetDItem(thebox, theGetItem, &itemtype, &thandle, &itemrect);
- return((ControlHandle)thandle);
- }
-
- /* end SnatchHandle */
-
- /* puts leading spaces as needed */
- /* replaces <# # # # #> in FORTH */
-
- static void WritePicturedNum(long theNum, short width)
- {
- Str32 theNumber;
- short spAdd = 0;
- NumToString(theNum, &theNumber);
- if (width <= 31) {
- if (theNumber[0] != width) {
- /* padd until */
- /* shift over some */
- spAdd = width - theNumber[0];
- BlockMove((Ptr)&theNumber[1], (Ptr)&theNumber[spAdd + 1], theNumber[0]);
-
- while (spAdd) {
- theNumber[spAdd] = '0';
- spAdd--;
- }
- theNumber[0] = width;
- }
- }
- DrawString(theNumber);
-
- } /* end WritePicturedNum */
-
- /* util */
-
- /* BorderDefault draws a heavy border around the default button (in this case the OK button ) */
- /* Not necessary if SetDefaultItem is used in System 7 (see DialogBits 2.0.1) */
-
- static pascal void BorderDefault(DialogPtr dwind, short dinum)
- {
- #pragma unused (dinum)
- short itemtype;
- Handle itemhandle;
- Rect borderRect;
- GetDItem(dwind, ok, &itemtype, &itemhandle, &borderRect);
- /* ok is defined as 1 in the interfaces. If you'd like another item outlined, */
- /* change this number, of course. */
- InsetRect(&borderRect, -4, -4);
- PenSize(3, 3);
- FrameRoundRect(&borderRect, 16, 16);
- PenSize(1, 1);
- }
-
- /* end BorderDefault */
-
- /* Replaces constantly calling Get/Draw string */
-
- static void DrawIndString(short strList, short index)
- {
- Str255 theString;
- GetIndString(theString, strList, index);
- DrawString(theString);
-
- }
-
- /* end DrawIndString */
-
- /* DateBox actually draws the two dates in the userItem rect set up for them */
-
- static pascal void DateBox(WindowPtr rDial, short dinum)
- {
- #pragma unused (dinum )
- short kindI, oldFont;
- Handle HandL;
- Rect itemRect;
- Rect thiRect;
- short tWide, tHi;
- short tenFnum, oldSize;
- PicHandle theHittedThing;
- short index = 0;
-
- GetDItem(rDial, kManDateItem, &kindI, &HandL, &itemRect);
- EraseRect(&itemRect);
-
- /* I am using Monaco here because it's a monospaced font, and I want */
- /* the date things not to shift all the time by characters changing width */
-
- GetFNum("\pMonaco", &tenFnum);
-
- /* save the old font and size */
- oldFont = rDial->txFont;
- oldSize = rDial->txSize;
-
- /* set the new font and size */
- TextFont(tenFnum);
- TextSize(12);
-
- /* plot our numbers */
- MoveTo(itemRect.left, itemRect.bottom - 7);
-
- /* I'm doing getpens so I can make the correct inversion and hit testing rectangles */
-
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.startDate.month, 2);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
- index++;
-
- DrawChar('/');
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.startDate.day, 2);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
- index++;
-
- DrawChar('/');
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.startDate.year, 4);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
- index++;
-
- DrawIndString(kGeneralStrings, kToWord);
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.endDate.month, 2);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
- index++;
-
- DrawChar('/');
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.endDate.day, 2);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
- index++;
-
- DrawChar('/');
- GetPen((Point *)&(dialogDateStruct.charBox[index].top));
- WritePicturedNum(dialogDateStruct.dialogDate.endDate.year, 4);
- GetPen((Point *)&(dialogDateStruct.charBox[index].bottom));
-
- /* draw the arrow picture thing */
- /* assumes itemRect && index ain't changed, so don't mess 'em up before here */
- /* get my picture of the up/down arrows */
- theHittedThing = GetPicture(129);
- thiRect = (*theHittedThing)->picFrame;
-
- /* get the basic dimensions of the picture */
- tWide = thiRect.right - thiRect.left;
- tHi = thiRect.bottom - thiRect.top;
-
- /* move it to after the last year */
- thiRect.left = dialogDateStruct.charBox[index].right + 5;
- thiRect.right = thiRect.left + tWide;
- thiRect.bottom = itemRect.bottom - 7;
- thiRect.top = thiRect.bottom - tHi;
-
- DrawPicture(theHittedThing, &thiRect);
- /* reset arrowHits */
- dialogDateStruct.arrowHits[0] = thiRect;
- dialogDateStruct.arrowHits[1] = thiRect;
-
- dialogDateStruct.arrowHits[0].bottom = dialogDateStruct.arrowHits[0].top + ((dialogDateStruct.arrowHits[0].bottom -
- dialogDateStruct.arrowHits[0].top) / 2);
- dialogDateStruct.arrowHits[1].top = dialogDateStruct.arrowHits[0].bottom + 1; /* slight slop */
- ReleaseResource((Handle)theHittedThing);
-
- for (index = 0; index < 6; index++) {
- FontInfo theFInfo;
- GetFontInfo(&theFInfo);
- /* set the top and bottom of the character rectangles */
- dialogDateStruct.charBox[index].bottom += 4;
- dialogDateStruct.charBox[index].top = dialogDateStruct.charBox[index].bottom - theFInfo.ascent - theFInfo.descent -
- theFInfo.leading;
-
- /* invert the current one so the user knows it's active */
- if (dialogDateStruct.editHot && dialogDateStruct.whichHitting == index)
- InvertRect(&dialogDateStruct.charBox[index]);
- }
- /* reset to the old font and size */
- TextFont(oldFont);
- TextSize(oldSize);
-
- }
-
- /* end DateBox */
-
- /* adjusts the current date if a key has been hit, mouse clicked, whatever */
- /* adding some extra stuff for using this from keystrokes */
- static void AdjustDate(short incOr, DialogPtr rDial, short newVal)
- {
- short *theThing;
- DateStruct originalDate;
- RgnHandle oldClip, currentClip;
- short amt;
-
- /* save the initial months for rollover checking */
- originalDate = dialogDateStruct.dialogDate;
-
- /* switch off which unit we're currently tracking */
- switch (dialogDateStruct.whichHitting) {
- case kStartMonth:
- theThing = (short *)&dialogDateStruct.dialogDate.startDate.month;
- break;
- case kStartDay:
- theThing = (short *)&dialogDateStruct.dialogDate.startDate.day;
- break;
- case kStartYear:
- theThing = (short *)&dialogDateStruct.dialogDate.startDate.year;
-
- break;
- case kEndMonth:
- theThing = (short *)&dialogDateStruct.dialogDate.endDate.month;
-
- break;
- case kEndDay:
- theThing = (short *)&dialogDateStruct.dialogDate.endDate.day;
- break;
- case kEndYear:
- theThing = (short *)&dialogDateStruct.dialogDate.endDate.year;
- break;
- }
- /* dammit! oh, nevermind, I figured it out */
- if (newVal < 0) {
- /* set the increment to plus one or minus 1 */
- amt = (incOr ? -1 : 1);
- /* and change the value */
- *theThing += amt;
- } else {
- if (dialogDateStruct.whichHitting == kStartYear || dialogDateStruct.whichHitting == kEndYear) {
- /* I forget why this is here, but I must have had a good reason */
- } else {
- short oldThing;
- /* ok, dammit, if it's a two-stroker then do 2 stroke things */
- /* Which means the user might have hit two numbers quickly (like 1 0 to indicate 10) */
- /* so we have to handle that, instead of treating every numeric stroke as a */
- /* number in the 1's column */
- if (TickCount() < dialogDateStruct.lastLetterTick) {
- oldThing = *theThing;
- *theThing *= 10;
- /* check range for months, forget days */
- if (dialogDateStruct.whichHitting == kStartMonth || dialogDateStruct.whichHitting == kEndMonth) {
-
- if ((*theThing + newVal) < 13) {
- /* if over 13, then reset it */
- newVal = *theThing + newVal;
- }
- } else {
- /* Should we do days? OK, but forget february */
- if ((*theThing + newVal) < 32) {
- newVal = *theThing + newVal;
- }
- }
- }
- *theThing = newVal;
-
- /* set up for the second in a two-key number entry. They have to hit the second */
- /* key within 60 ticks for it to count with the first one */
- dialogDateStruct.lastLetterTick = TickCount() + 60;
- }
- }
- /* let the utils figure out if they wrapped or not */
-
- Date2Secs(&dialogDateStruct.dialogDate.startDate, &dialogDateStruct.dialogDate.startDateraw);
- Date2Secs(&dialogDateStruct.dialogDate.endDate, &dialogDateStruct.dialogDate.endDateraw);
- /* HAHAHA and now the other way */
- Secs2Date(dialogDateStruct.dialogDate.startDateraw, &dialogDateStruct.dialogDate.startDate);
- Secs2Date(dialogDateStruct.dialogDate.endDateraw, &dialogDateStruct.dialogDate.endDate);
-
- /* check for rollovers */
- /* I _think_ the only rollover occurs with days, so check that only */
- /* Nope, have to check for months rolling over years and days */
- /* Stop being inefficient, and just keep building a bigger region, OK? */
-
- /* Save the old clipping region, and set up a new one that */
- /* will include just the rectangles that change */
- oldClip = NewRgn();
- GetClip(oldClip);
- currentClip = NewRgn();
- OpenRgn();
-
- /* clip to the currently hitting character */
- FrameRect(&dialogDateStruct.charBox[dialogDateStruct.whichHitting]);
-
- /* now go through any side-effects and clip to them also */
- /* Side effects like days rolling past 31 and the month changing */
-
- switch (dialogDateStruct.whichHitting) {
- case kStartMonth:
- /* Did the year or day change because of this month change? */
- if (originalDate.startDate.year != dialogDateStruct.dialogDate.startDate.year)
- FrameRect(&dialogDateStruct.charBox[kStartYear]);
- if (originalDate.startDate.day != dialogDateStruct.dialogDate.startDate.day)
- FrameRect(&dialogDateStruct.charBox[kStartDay]);
-
- break;
- case kStartDay:
- /* Did the month change because of this day change? */
-
- if (originalDate.startDate.month != dialogDateStruct.dialogDate.startDate.month)
- FrameRect(&dialogDateStruct.charBox[kStartMonth]);
-
- break;
-
- case kEndMonth:
- /* Did the year or day change because of this month change? */
- if (originalDate.endDate.year != dialogDateStruct.dialogDate.endDate.year)
- FrameRect(&dialogDateStruct.charBox[kEndYear]);
- if (originalDate.endDate.day != dialogDateStruct.dialogDate.endDate.day)
- FrameRect(&dialogDateStruct.charBox[kEndDay]);
-
-
- break;
- case kEndDay:
- /* Did the month change because of this day change? */
-
- if (originalDate.endDate.month != dialogDateStruct.dialogDate.endDate.month)
- FrameRect(&dialogDateStruct.charBox[kEndMonth]);
-
- break;
-
- }
- CloseRgn(currentClip);
-
- /* Clip to these boxes */
- SetClip(currentClip);
-
- /* redraw the date */
- DateBox(rDial, kManDateItem);
-
- /* restore original clip */
- SetClip(oldClip);
-
- /* dispose of our temp regions */
- DisposeRgn(currentClip);
- DisposeRgn(oldClip);
- }
-
- /* end AdjustDate */
-
- /* DateThingerFilter */
- /* Filters events for the date dialog */
-
- static pascal Boolean DateThingerFilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
- {
- #pragma unused (itemHit )
- static enum {
- kGoingUp = 1, kGoingDown
- };
- Boolean retVal = false;
- WindowPtr theOld;
- unsigned long qq;
- short kindI;
- short upOrDown = 0;
- Handle HandL;
- Rect itemRect;
- long tempLong;
- Point thePoint;
- GetPort(&theOld);
- SetPort(theDialog);
- if (theEvent->what == mouseDown) {
-
- /* if the event is a mouse down, it could be in a number or an arrow */
- GetMouse(&thePoint);
-
- /* check arrow box first */
- if (PtInRect(thePoint, &dialogDateStruct.arrowHits[0])) {
- upOrDown = kGoingUp;
- } else {
- if (PtInRect(thePoint, &dialogDateStruct.arrowHits[1])) {
- upOrDown = kGoingDown;
- }
- }
- /* if upOrDown was set, then it was in a arrow */
- if (upOrDown) {
- /* for the arrow picture */
-
- InvertRect(&dialogDateStruct.arrowHits[upOrDown - 1]);
- /* stay with it and blink */
- Delay(8, &tempLong);
-
- while (StillDown()) {
- /* if they are holding down in the arrow thing, then wait 8 ticks */
- /* for them to come up, if they don't adjust again */
- /* making this a subroutine to keep the clutter out of this filter */
- AdjustDate(upOrDown - 1, theDialog, -1);
- Delay(8, &tempLong);
- }
- InvertRect(&dialogDateStruct.arrowHits[upOrDown - 1]);
-
- } else {
- /* they didn't click in an arrow, maybe they clicked in a number */
- for (qq = 0; qq < 6; qq++) {
- /* see if in the hot numbers */
- if (PtInRect(thePoint, &dialogDateStruct.charBox[qq])) {
- dialogDateStruct.editHot = true;
- dialogDateStruct.whichHitting = qq;
-
- /* invalidate the whole date so the new number */
- /* gets inverted and the old one loses it's invert */
- GetDItem(theDialog, kManDateItem, &kindI, &HandL, &itemRect);
- InvalRect(&itemRect);
- retVal = true;
- break;
- }
- }
- }
- } else {
-
- /* see if they entered a number or arrow or tab */
- if (theEvent->what == keyDown) {
- char theKey = theEvent->message &charCodeMask;
-
- if (theKey == kTabKey && dialogDateStruct.editHot) {
- /* OK dang it, I'll add shift-reverse tabbing. grumble grumble */
-
- /* blow away the lastKey ticks */
-
- dialogDateStruct.lastLetterTick = 0;
- InvalRect(&dialogDateStruct.charBox[dialogDateStruct.whichHitting]);
-
- if (theEvent->modifiers & shiftKey)
- dialogDateStruct.whichHitting--;
- else
- dialogDateStruct.whichHitting++;
-
- /* wrap around on tabbing */
- if (dialogDateStruct.whichHitting >= 6)
- dialogDateStruct.whichHitting = kStartMonth;
-
- if (dialogDateStruct.whichHitting < 0)
- dialogDateStruct.whichHitting = kEndYear;
-
- InvalRect(&dialogDateStruct.charBox[dialogDateStruct.whichHitting]);
-
- } else {
- /* see if it's a number, or heck, an arrow */
- if ((theKey > 0x2f && theKey < 0x3a) && dialogDateStruct.editHot) {
- /* hmmmm. Now what do we do? */
- /* I'm just going to stuff the number in the last digit of the year, or */
- /* the month/day */
- /* forget it, let the real thing figure it out */
- AdjustDate(nil, theDialog, theKey - 0x30);
-
- } else {
- /* do some other standard key stuff */
- switch (theKey) {
- short tempItem;
- Handle tempHandle;
- Rect tempRect;
-
- case kLeftArrowKey:
- case kDownArrowKey:
- upOrDown = kGoingDown;
- break;
- case kRightArrowKey:
- case kUpArrowKey:
- upOrDown = kGoingUp;
- break;
- case kEscKey:
- *itemHit = 2;
- GetDItem(theDialog, 1, &tempItem, &tempHandle, &tempRect);
-
- if (tempItem == ctrlItem) {
- /* much more likely that this may not be a button */
-
- HiliteControl(SnatchHandle(theDialog, cancel), inButton);
- Delay(8, &tempLong); /* wait about 8 ticks so they can see it */
- HiliteControl(SnatchHandle(theDialog, cancel), false);
- retVal = true;
- }
- break;
- case kReturnKey:
- case kEnterKey: /* enter key */
- /* This filters for Return or Enter as item 1, and Esc as item 2 */
- *itemHit = 1; /* change whatever the current item is to the OK item */
- /* now we need to invert the button */
- GetDItem(theDialog, 1, &tempItem, &tempHandle, &tempRect);
- if (tempItem == ctrlItem) {
- /* double check for the unlikely event that this is _not_ a button */
- HiliteControl(SnatchHandle(theDialog, ok), inButton);
- Delay(8, &tempLong); /* wait about 8 ticks so they can see it */
- HiliteControl(SnatchHandle(theDialog, ok), false);
-
- retVal = true;
- }
- break;
-
- default:
- /* nothing */
- break;
- }
- }
- }
- }
- }
-
- /* did anything actually happen? */
- if (upOrDown)
- AdjustDate(upOrDown - 1, theDialog, -1);
-
- SetPort(theOld);
- return(retVal);
- }
-
- /* end DateThingerFilter */
-
- /* DateSetterDialog */
- /* actually displays and runs the dialog */
- void DateSetterDialog(void)
- {
- DialogPtr tdial = GetNewDialog(kDateDialog, nil, (WindowPtr)-1);
- short hitItem;
- Boolean noExit = true;
- short kindI;
- Handle HandL;
- Rect itemRect;
-
- /* set up our date thing user item */
- GetDItem(tdial, kManDateItem, &kindI, &HandL, &itemRect);
- SetDItem(tdial, kManDateItem, userItem, (Handle)DateBox, &itemRect);
-
- /* default some values */
- dialogDateStruct.editHot = true;
- dialogDateStruct.whichHitting = 0;
- GetDateTime(&dialogDateStruct.dialogDate.startDateraw);
- GetDateTime(&dialogDateStruct.dialogDate.endDateraw);
- Secs2Date(dialogDateStruct.dialogDate.startDateraw, &dialogDateStruct.dialogDate.startDate);
- Secs2Date(dialogDateStruct.dialogDate.endDateraw, &dialogDateStruct.dialogDate.endDate);
-
- /* set up for bordering OK */
- GetDItem(tdial, kBorderBox, &kindI, &HandL, &itemRect);
- SetDItem(tdial, kBorderBox, kindI, (Handle)BorderDefault, &itemRect);
-
- /* dialog was created hidden, so show it */
- ShowWindow(tdial);
- DrawDialog(tdial);
-
- do {
- ModalDialog((ModalFilterProcPtr)DateThingerFilter, &hitItem);
- switch (hitItem) {
- case ok:
- case cancel:
- noExit = false;
- break;
- }
- }
- while (noExit);
- if (hitItem == ok) {
-
- }
- DisposDialog(tdial);
-
- }
-
- /* end DateSetterDialog */
-